package org.xlp.db.xml;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xlp.assertion.AssertUtils;
import org.xlp.db.xml.ele.SQLEle;
import org.xlp.utils.XLPArrayUtil;

import java.io.File;
import java.io.FilenameFilter;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 处理sql配置文件应用
 * @version 1.0
 * @author xlp
 */
public class SQLXmlContextApplication implements ISQLXmlFileHandlerList, ISQLXmlParser {
    private static final Logger LOGGER = LoggerFactory.getLogger(SQLXmlContextApplication.class);

    /**
     * {@link SQLXmlFileHandlerChain} 对象创建工厂
     */
    private SQLXmlFileHandlerChainFactory chainFactory = new SQLXmlFileHandlerChainFactory();

    /**
     * 存储所有的SQLEle对象
     */
    private Map<String, SQLEle> sqlEleMap = new HashMap<>();

    /**
     * 标记是否忽视重复的SQLEle对象，true：忽视，false：不忽视
     */
    private boolean ignoreSameSQLEle = false;
    
    /**
     * 构造函数
     */
    public SQLXmlContextApplication(){}
    
    /**
     * 构造函数
     * @param chainFactory {@link SQLXmlFileHandlerChain} 对象创建工厂
     */
    public SQLXmlContextApplication(ISQLXmlFileHandlerChainFactory chainFactory){
    	setChainFactory(chainFactory);
    }

    /**
     * 设置{@link SQLXmlFileHandlerChain} 对象创建工厂
     * @param chainFactory
     */
    private void setChainFactory(ISQLXmlFileHandlerChainFactory chainFactory) {
        if (chainFactory != null) {
            if (chainFactory instanceof SQLXmlFileHandlerChainFactory){
                this.chainFactory = (SQLXmlFileHandlerChainFactory) chainFactory;
            } else {
                this.chainFactory = newSQLXmlFileHandlerChainFactory(chainFactory);
            }
        }
    }

    /**
     * 更据给定的ISQLXmlFileHandlerChainFactory对象获取{@link SQLXmlFileHandlerChain} 对象创建工厂
     * @param chainFactory
     * @return
     */
    private static SQLXmlFileHandlerChainFactory newSQLXmlFileHandlerChainFactory(
            ISQLXmlFileHandlerChainFactory chainFactory) {
        return new SQLXmlFileHandlerChainFactory() {
            @Override
            public ISQLXmlFileHandlerChain createSQLXmlFileHandlerChain() {
                return chainFactory.createSQLXmlFileHandlerChain();
            }
        };
    }

    /**
     * 是否忽视重复的SQLEle对象，true：忽视，false：不忽视
     * @return
     */
    public boolean isIgnoreSameSQLEle() {
        return ignoreSameSQLEle;
    }

    /**
     * 设置是否忽视重复的SQLEle对象，true：忽视，false：不忽视
     * @param ignoreSameSQLEle true/false
     */
    public void setIgnoreSameSQLEle(boolean ignoreSameSQLEle) {
        this.ignoreSameSQLEle = ignoreSameSQLEle;
    }

    /**
     * 添加处理器
     *
     * @param index   向指定索引添加处理器
     * @param handler 要添加的处理器
     * @throws NullPointerException 假如参数 handler 为null，则抛出该异常
     */
    @Override
    public void addHandler(int index, ISQLXmlFileHandler handler) {
        chainFactory.addHandler(index, handler);
    }

    /**
     * 添加处理器
     *
     * @param handler 要添加的处理器
     * @throws NullPointerException 假如参数 handler 为null，则抛出该异常
     */
    @Override
    public void addHandler(ISQLXmlFileHandler handler) {
        chainFactory.addHandler(handler);
    }

    /**
     * 向头部添加处理器
     *
     * @param handler 要添加的处理器
     * @throws NullPointerException 假如参数 handler 为null，则抛出该异常
     */
    @Override
    public void addHandlerToHead(ISQLXmlFileHandler handler) {
        chainFactory.addHandlerToHead(handler);
    }

    /**
     * 移除指定位置的处理器
     *
     * @param index
     * @throws IllegalAccessException 假如参数不合理，则抛出该异常
     */
    @Override
    public void removeHandler(int index) {
        chainFactory.removeHandler(index);
    }

    /**
     * 移除指定的sql处理器
     *
     * @param handler
     * @throws NullPointerException 假如参数 handler 为null，则抛出该异常
     */
    @Override
    public void removeHandler(ISQLXmlFileHandler handler) {
        chainFactory.removeHandler(handler);
    }

    /**
     * 移除所有的处理器
     */
    @Override
    public void clearHandler() {
        chainFactory.clearHandler();
    }

    /**
     * 获取所有的sql处理器
     *
     * @return
     */
    @Override
    public List<ISQLXmlFileHandler> getAllHandlers() {
        return chainFactory.getAllHandlers();
    }

    /**
     * 解析sql xml配置文件路径
     * @param filePath sql xml配置文件路径
     * @throws IllegalArgumentException 假如参数不是xml文件路径，则抛出该异常
     * @throws NullPointerException 假如参数为null或""，则抛出该异常
     */
    @Override
    public void parseFile(String filePath){
        try {
            _parseFile(filePath);
        } finally {
            HasParseFileCache.clearCache();
        }
    }

    /**
     * 解析sql xml配置文件路径
     * @param filePath sql xml配置文件路径
     * @throws IllegalArgumentException 假如参数不是xml文件路径，则抛出该异常
     * @throws NullPointerException 假如参数为null或""，则抛出该异常
     */
    private void _parseFile(String filePath){
        if (HasParseFileCache.hasParse(filePath)){
            if (LOGGER.isDebugEnabled()){
                LOGGER.debug("该【" + filePath + "】已解析过，不在解析！");
            }
            return;
        }
        AssertUtils.isNotNull(filePath, "filePath parameter must not be null or empty!");
        if (!filePath.endsWith(FilePathPrefixConstant.XML_FILE_SUFFIX)){
            throw new IllegalArgumentException("要解析的文件不是xml文件");
        }

        boolean debugEnabled = LOGGER.isDebugEnabled();
        long startTime = 0;
        if (debugEnabled){
            LOGGER.debug("开始解析" + filePath);
            startTime = System.currentTimeMillis();
        }

        ISQLXmlFileHandlerChain chain = chainFactory.createSQLXmlFileHandlerChain();
        SQLConfigurationFileInfo info = chain.handle(filePath);
        if (info == null) return;

        List<SQLsElementInfo> sqlsElementInfos = info.getSqlsElementInfos();
        for (SQLsElementInfo sqlsElementInfo : sqlsElementInfos) {
            List<SQLEle> sqlEles = sqlsElementInfo.getSqlEles();
            for (SQLEle sqlEle : sqlEles) {
                //判断是否忽略重复的SQLEle对象
                String key = mapKey(sqlEle.getParentId(), sqlEle.getId());
                SQLEle sqlEle1;
                if (!ignoreSameSQLEle && (sqlEle1 = sqlEleMap.get(key)) != null){
                    throw new SQLXmlFileParseException(sqlEle1.getFileDescription() + "|与|"
                            + sqlEle.getFileDescription() + "有parentId = " + sqlEle.getParentId()
                            + ", id=" + sqlEle.getId() + "相同的SQLEle对象");
                }
                sqlEleMap.put(key, sqlEle);
            }
        }

        if (debugEnabled){
            LOGGER.debug("解析完：" + filePath + "，所用时间为：" + (System.currentTimeMillis() - startTime));
        }

        //处理其他sql配置文件
        for (String path : info.getSqlConfigurationFilePaths()) {
            //递归处理其他sql配置文件
            _parseFile(path);
        }
    }

    /**
     * 解析给定的目录下所有的sql xml配置文件
     * @param directory sql xml配置文件目录
     * @throws IllegalArgumentException 假如参数不是文件目录，则抛出该异常
     * @throws NullPointerException 假如参数为null，则抛出该异常
     */
    @Override
    public void parseDirectory(File directory){
        AssertUtils.isNotNull(directory, "directory parameter must not be null!");
        if (!directory.exists() || directory.isFile()){
            throw new IllegalArgumentException("给定的文件不是目录：" + directory );
        }
        try {
            _parseDirectory(directory);
        } finally {
            HasParseFileCache.clearCache();
        }
    }
    
    /**
     * 解析给定的目录下所有的sql xml配置文件
     * @param directory sql xml配置文件目录
     */
    private void _parseDirectory(File directory){
        File[] files = directory.listFiles(new FilenameFilter() {
			@Override
			public boolean accept(File dir, String name) {
				return name.endsWith(FilePathPrefixConstant.XML_FILE_SUFFIX);
			}
		});
        //假如目录没有文件，则直接返回
        if (XLPArrayUtil.isEmpty(files)) return;
        
        for (File f : files) { 
			if (f.isFile()) {
				_parseFile(f.getAbsolutePath());
			} else {
				_parseDirectory(f);
			}
		}
    }

    /**
     * 获取通过sql语句配置文件解析出的所有SQLEle对象
     * @return
     */
    public Map<String, SQLEle> getSqlEleMap() {
        return Collections.unmodifiableMap(sqlEleMap);
    }

    /**
     * 更据Id获取相应的{@link SQLEle}对象
     * @see SQLsElementInfo
     * @see SQLEle
     * @param parentId {@link SQLsElementInfo}的id值
     * @param id {@link SQLEle}的id值
     * @return {@link SQLEle}对象
     * @throws NullPointerException 假如参数为null或""，则抛出该异常
     */
    @Override
    public SQLEle getSqlEle(String parentId, String id){
        AssertUtils.isNotNull(parentId, "parentId parameter must not be null or empty!");
        AssertUtils.isNotNull(id, "id parameter must not be null or empty!");
        return sqlEleMap.get(mapKey(parentId.trim(), id.trim()));
    }

    /**
     * 获取{@link SQLXmlContextApplication#sqlEleMap}的key
     * @param parentId
     * @param id
     * @return
     */
    private String mapKey(String parentId, String id){
        return parentId + "_" + id;
    }
}
